package com.flow.framework.persistence.service.system.health.impl;

import com.flow.framework.common.health.ServiceHealthCheckCode;
import com.flow.framework.common.pojo.vo.system.ServiceHealthVo;
import com.flow.framework.common.service.system.health.IHealthCheckService;
import com.flow.framework.common.util.verify.VerifyUtil;
import com.flow.framework.core.system.helper.SystemHealthHelper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.jdbc.DatabaseDriver;
import org.springframework.jdbc.IncorrectResultSetColumnCountException;
import org.springframework.jdbc.core.ConnectionCallback;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.JdbcUtils;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;

/**
 * 数据库健康检查
 *
 * @author luoguopiao
 * @version 0.0.1
 * @date 2022/4/4
 */
@Slf4j
@RequiredArgsConstructor
public class DataSourceHealthCheckServiceImpl implements InitializingBean, IHealthCheckService {

    private static final String DEFAULT_QUERY = "SELECT 1";

    private final DataSource dataSource;

    private String query;

    private JdbcTemplate jdbcTemplate;

    /**
     * @inheritDoc
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
        String product = getProduct();
        DatabaseDriver specific = DatabaseDriver.fromProductName(product);
        String temp = specific.getValidationQuery();
        if (!VerifyUtil.isEmpty(temp)) {
            this.query = temp;
        } else {
            this.query = DEFAULT_QUERY;

        }

    }

    /**
     * @inheritDoc
     */
    @Override
    public List<ServiceHealthVo> check() {
        try {
            List<Object> results = this.jdbcTemplate.query(query, new SingleColumnRowMapper());
            if (VerifyUtil.isEmpty(results) || results.size() != 1) {
                log.error("check database error. query result : {}", results);
                return Collections.singletonList(
                        SystemHealthHelper.unhealthy(ServiceHealthCheckCode.SERVICE_DATABASE_CODE, results.toString(), null)
                );
            }
            return Collections.singletonList(SystemHealthHelper.healthy(ServiceHealthCheckCode.SERVICE_DATABASE_CODE));
        } catch (Exception e) {
            log.error("check database error.", e);
            return Collections.singletonList(
                    SystemHealthHelper.unhealthy(ServiceHealthCheckCode.SERVICE_DATABASE_CODE, e.getMessage(), null)
            );
        }
    }

    private String getProduct() {
        return this.jdbcTemplate.execute((ConnectionCallback<String>) this::getProduct);
    }

    private String getProduct(Connection connection) throws SQLException {
        return connection.getMetaData().getDatabaseProductName();
    }

    private static class SingleColumnRowMapper implements RowMapper<Object> {

        @Override
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
            ResultSetMetaData metaData = rs.getMetaData();
            int columns = metaData.getColumnCount();
            if (columns != 1) {
                throw new IncorrectResultSetColumnCountException(1, columns);
            }
            return JdbcUtils.getResultSetValue(rs, 1);
        }

    }
}
